﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LC.Utils
{
    class Program
    {
        /// <summary>
        /// Sample usage
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            BaseAnythingConvert convert = new BaseAnythingConvert(ConversionModeEnum.Hex);

            for (int val = 0; val < int.MaxValue; val++)
            {
                string Test1 = convert.ToBaseN(val, 8);
                int Test3 = convert.ToInt(Test1);

                //
                // The following method is not implemented.  It should be possible to compute a minimum length 
                // for a given conversion.  I'll ask the open source community to contribute the solution at
                //  http://baseanythingconvert.codeplex.com/
                //
                // string Test2 = convert.ToBaseN(val);

                Console.WriteLine(val + "  " + Test1  + " " + Test3);
            }
            Console.ReadKey();

        }
    }

    public enum ImplementationEnum
    {
        /// <summary>
        /// Caution this is an experimental conversion method.
        /// </summary>
        [Obsolete("The Linq implementation is experimental.  Caution: Will not work with ToInt() function.")]
        Linq,
        TwoChar,
        Variable
    }

    public enum ConversionModeEnum
    {
        Binary,
        Hex,
        Compress,
        [Obsolete("Not yet implemented")]
        Unicode,

        [Obsolete("Not yet implemented")]
        AzureTableProperty,
    }

    public class BaseAnythingConvert
    {
        public BaseAnythingConvert()
        {
            this._BaseNChars = _BaseNCharsHex;
        }
        /// <summary>
        /// This overload will allow you to specify a custom base conversion.  If you want to be creative you can also use Unicode characters.
        /// </summary>
        /// <param name="BaseChars"></param>
        public BaseAnythingConvert(string BaseChars)
        {
            this._BaseNChars = BaseChars;
        }
        public BaseAnythingConvert(ConversionModeEnum mode)
        {
            switch (mode)
            {
                case ConversionModeEnum.Binary:
                    this._BaseNChars = _BaseNCharsBinary;
                    break;
                case ConversionModeEnum.Hex:
                    this._BaseNChars = _BaseNCharsHex;
                    break;
                case ConversionModeEnum.Compress:
                    this._BaseNChars = _BaseNCharsCompress;
                    break;
                case ConversionModeEnum.Unicode:
                    this._BaseNChars = _BaseNCharsCompress;
                    break;
                case ConversionModeEnum.AzureTableProperty:
                    this._BaseNChars = _BaseNCharsCompress;
                    break;
                default:
                    break;
            }
        }

        string _BaseNCharsCompress = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
        string _BaseNCharsHex = "0123456789ABCDEF";
        string _BaseNCharsBinary = "01";

        string _BaseNChars { get; set; }

        int ToInt(string BaseNValue, ImplementationEnum implementationEnum)
        {
            switch (implementationEnum)
            {
                case ImplementationEnum.Linq:
                    {
                        // Experimental, doesn't work.
                        //return ToIntegerLinq(BaseNValue);

                        throw new NotSupportedException("Linq is not supported for the ToInt function.");
                    }
                case ImplementationEnum.TwoChar:
                    {
                        return ToIntegerMax2Char(BaseNValue);
                    }
                case ImplementationEnum.Variable:
                    {
                        return ToIntFromStringGood(BaseNValue);
                    }
                default:
                    throw new InvalidOperationException("conversion type unknown");
            }
        }
        string ToBaseN(int val, int? p, ImplementationEnum implementationEnum)
        {
            switch (implementationEnum)
            {
                case ImplementationEnum.Linq:
                    {
                        return ToCharArrayLinq(3, val);
                    }
                case ImplementationEnum.TwoChar:
                    {
                        if (p.HasValue)
                            if (p != 2)
                                throw new ArgumentOutOfRangeException("When using TwoChar code, you must have null or 2 as the length");

                        // if not max value for 2 chars 
                        if (true)
                        {
                            return ToCharArrayMax2Char(val);
                        }
                        break;
                    }
                case ImplementationEnum.Variable:
                    {
                        return ToCharArrayGood(p, val);
                    }
                default:
                    return null;
            }

        }


        int ToIntegerLinq(string str)
        {
            return ToCharArrayLinq(str.Length).SkipWhile(s => s != str).Count();
        }
        IEnumerable<string> ToCharArrayLinq(int desiredLength)
        {
            if (desiredLength == 0)
                return new[] { "" };
            IEnumerable<string> tmp = _BaseNChars.ToCharArray().SelectMany(ch => ToCharArrayLinq(desiredLength - 1)
                                                .Select(str => ch + str));

            return tmp;
        }


        string ToCharArrayLinq(int desiredLength, int val)
        {
            return String.Join("", ToCharArrayLinq(desiredLength).Skip(val).Take(1));
        }
        string ToCharArrayMax2Char(int value)
        {
            // if (value > 3721)
            //   return "zz";

            char l = _BaseNChars[value / _BaseNChars.Length];
            char r = _BaseNChars[value % _BaseNChars.Length];
            return new string(new char[] { l, r });
        }

        int ToIntegerMax2Char(string chars)
        {
            int l = _BaseNChars.IndexOf(chars[0]);
            int r = _BaseNChars.IndexOf(chars[1]);
            return (l * _BaseNChars.Length) + r;
        }


        string ToCharArrayGood(int? digitCount, int n)
        {
            int maxdigit = 0;
            if (digitCount.HasValue == false)
                maxdigit = 1;
            else
                maxdigit = digitCount.Value;

            var number = String.Empty;
            for (var i = 0; i < maxdigit; ++i)
            {
                number = _BaseNChars[n % _BaseNChars.Length] + number;
                n /= _BaseNChars.Length;
            }
            return number;
        }
        int ToIntFromStringGood(string number)
        {
            var n = 0;
            for (var i = 0; i < number.Length; ++i)
                n = n * _BaseNChars.Length + _BaseNChars.IndexOf(number[i]);
            return n;
        }


        IEnumerable<string> Increment(string str)
        {
            return ToCharArrayLinq(str.Length).SkipWhile(s => s != str).Skip(1).Take(1);
        }
        /// <summary>
        /// Convert the Hex value (such as C) into a number (such as 12)
        /// </summary>
        /// <param name="variableCharImplementation"></param>
        /// <returns></returns>
        public int ToInt(string variableCharImplementation)
        {
            return ToInt(variableCharImplementation, ImplementationEnum.Variable);
        }

        /// <summary>
        /// Unsupported, let me know if you are willing to contribute to get this overload to work.
        /// </summary>
        /// <param name="val"></param>
        /// <returns></returns>
        [Obsolete("This overload is not supported.  You must specify a length.  It is possible to make this function work, but I'll ask for your assistance here.", true)]
        public string ToBaseN(int val)
        {
            return ToBaseN(val, null, ImplementationEnum.Variable);
        }

        /// <summary>
        /// Convert a number such as 12 into the hex value of C
        /// </summary>
        /// <param name="val"></param>
        /// <param name="characterCount">Specify the number of characters to return.  Useful when you need to have leading zeros.</param>
        /// <returns></returns>
        public string ToBaseN(int val, int p)
        {
            return ToBaseN(val, p, ImplementationEnum.Variable);
        }
    }
}
